package com.xxg.socket.impl;

import com.xxg.socket.YmaSession;
import com.xxg.socket.YmlServerConfig;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Created by game group on 2020/12/23.
 */
public abstract class NettySocketServer extends SessionAbleYmaServer {
    private static final Logger log = LoggerFactory.getLogger(NettySocketServer.class);

    private final YmlServerConfig config;
    private Channel serverChannel;

    public NettySocketServer(YmlServerConfig config){
        this.config = config;
    }

    public NettySocketServer(){
        this(new DefaultServerConfig());
    }

    @Override
    public void start(){
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(config.getBoosGroup(),config.getWorkerGroup())
                .channel(NioServerSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel channel) throws Exception {
                        channel.pipeline()
                                .addLast(new ByteArrayEncoder())
                                .addLast(new ByteArrayDecoder())
                                .addLast(NettySocketServer.this);
                    }
                });

        ChannelFuture future = bootstrap.bind(config.getSocketAddress());
        future.addListener(f -> {
            if(f.isDone() && f.isSuccess()){
                this.serverChannel = future.channel();
                log.info("Start ws server success");
                log.info("boos group thread number {}",((NioEventLoopGroup) config.getBoosGroup()).executorCount());
                log.info("worker group thread number {}",((NioEventLoopGroup) config.getWorkerGroup()).executorCount());
            }
            if(f.isDone() && f.cause() != null){
                log.error("Start ws server fail throw={}", f.cause().getMessage());
                future.channel().close();
            }
        });
    }

    @Override
    public void stop(){
        if(serverChannel != null && serverChannel.isOpen()){
            final int waitSec = 10;
            CountDownLatch latch = new CountDownLatch(1);
            serverChannel.close().addListener(f -> {
                config.getWorkerGroup().schedule(() -> {
                    log.info("Shutdown dispatcher success...");
                    config.getWorkerGroup().shutdownGracefully();
                    latch.countDown();
                }, waitSec - 2, TimeUnit.SECONDS);

                log.info("Close ws server socket success={}", f.isSuccess());
                config.getBoosGroup().shutdownGracefully();
            });

            try{
                latch.await(waitSec, TimeUnit.SECONDS);
            }catch (InterruptedException e){
                log.warn("Shutdown ws server interrupted exception={}", e.getMessage());
            }
        }
    }

    @Override
    public void onError(YmaSession session, Throwable e) {

    }

    @Override
    public Object onClose(YmaSession session) {
        return null;
    }

    @Override
    public void onStart() {

    }

    @Override
    public void onStop() {

    }
}
